using System;
using System.Net;
using System.IO;
using System.Configuration;
using System.Collections.Specialized;

namespace OnlinePayments
{
	/// <summary>
	/// Summary description for AuthorizeDotNet.
	/// </summary>
	internal class AuthorizeDotNet : PaymentProcessor
	{
		private string _loginID;
		private string _transactionKey;
		private string _url;

		public AuthorizeDotNet( char processorCode, int ownerID ) : base(processorCode, ownerID)
		{
            _loginID = _configStrings["LoginID"];
            _transactionKey = _configStrings["TransactionKey"];
            _url = _configStrings["URL"];

			if ( _loginID == null || _transactionKey == null || _url == null )
				throw new Exception( "Missing data in the AuthorizeDotNet config section" );
		}

		public override bool Authorize( CustomerInfo customerInfo, 
			Int32 transactionID, 
			decimal amount, 
			out string responseCode,
			out string responseText,
			out string AVSResult,
			out string CVV2Response,
			out string processorTransactionID,
			out string authCode 
			)
		{
			AVSResult = null;
			CVV2Response = null;
			processorTransactionID = null;
			authCode = null;
			responseCode = null;
			responseText = null;

			if ( customerInfo.CardExpiration == null || customerInfo.CardExpiration == string.Empty )
			{
				responseCode = "7";
				responseText = "Missing expiration date";
				return false;
			}
			string request = 
				"&x_amount=" + amount.ToString() +
				"&x_card_num=" + customerInfo.CardNumber +
				"&x_exp_date=" + customerInfo.CardExpiration +
				"&x_type=AUTH_ONLY" + 
				"&x_first_name=" + UrlEncode( customerInfo.FirstName ) +
				"&x_last_name=" + UrlEncode( customerInfo.LastName ) + 
				"&x_address=" + UrlEncode( customerInfo.Address ) +
				"&x_city=" + UrlEncode( customerInfo.City ) +
				"&x_state=" + UrlEncode( customerInfo.State ) +
				"&x_zip=" + UrlEncode( customerInfo.Zip ) +
				"&x_country=" + UrlEncode( customerInfo.Country ) +
				"&x_invoice_num=" + transactionID.ToString();
			if ( customerInfo.CVV2 != null && customerInfo.CVV2 != "" )
				request += "&x_card_code=" + customerInfo.CVV2;
			StringDictionary results = readHtmlPage( request );

			responseCode = results["ResponseReasonCode"];
			responseText = results["ResponseReasonText"];
			AVSResult = results["AVSResultCode"];
			CVV2Response = results["CVV2ResponseCode"];
			processorTransactionID = results["TransactionID"];
			authCode = results["ApprovalCode"];

			return (results["ResponseCode"] == "1");
		}

		public override bool Deposit( CustomerInfo customerInfo, 
			Int32 transactionID, 
			decimal amount, 
			out string responseCode,
			out string responseText,
			out string AVSResult,
			out string CVV2Response,
			out string processorTransactionID 
			)
		{
			AVSResult = null;
			CVV2Response = null;
			processorTransactionID = null;
			responseCode = null;
			responseText = null;

            if (customerInfo.CardExpiration == null || customerInfo.CardExpiration == string.Empty)
            {
				responseCode = "7";
				responseText = "Missing expiration date";
				return false;
			}
			string request = 
				"&x_amount=" + amount.ToString() +
				"&x_invoice_num=" + transactionID.ToString() +
				"&x_card_num=" + customerInfo.CardNumber +
				"&x_exp_date=" + customerInfo.CardExpiration +
				"&x_type=AUTH_CAPTURE" + 
				"&x_first_name=" + UrlEncode( customerInfo.FirstName ) +
				"&x_last_name=" + UrlEncode( customerInfo.LastName ) + 
				"&x_address=" + UrlEncode( customerInfo.Address ) +
				"&x_city=" + UrlEncode( customerInfo.City ) +
				"&x_state=" + UrlEncode( customerInfo.State ) +
				"&x_zip=" + UrlEncode( customerInfo.Zip ) +
				"&x_country=" + UrlEncode( customerInfo.Country );
			if ( customerInfo.CVV2 != null && customerInfo.CVV2 != "" )
				request += "&x_card_code=" + customerInfo.CVV2;
			StringDictionary results = readHtmlPage( request );

			responseCode = results["ResponseReasonCode"];
			responseText = results["ResponseReasonText"];
			AVSResult = results["AVSResultCode"];
			CVV2Response = results["CVV2ResponseCode"];
			processorTransactionID = results["TransactionID"];

			return (results["ResponseCode"] == "1");
		}

		public override bool DepositWithPriorAuth( CustomerInfo customerInfo, 
			Int32 transactionID, 
			decimal amount, 
			string processorTransactionID,
			string authCode,
			out string responseCode,
			out string responseText,
			out string AVSResult,
			out string CVV2Response
			)
		{
			AVSResult = null;
			CVV2Response = null;
			responseCode = null;
			responseText = null;

            if (customerInfo.CardExpiration == null || customerInfo.CardExpiration == string.Empty)
            {
				responseCode = "7";
				responseText = "Missing expiration date";
				return false;
			}
			string request = 
				"&x_amount=" + amount.ToString() +
				"&x_invoice_num=" + transactionID.ToString() +
				"&x_card_num=" + customerInfo.CardNumber +
				"&x_exp_date=" + customerInfo.CardExpiration;
			// If the transaction ID is passed then use that otherwise use the auth code.
			if ( processorTransactionID == null || processorTransactionID == "" )
			{
				request += "&x_type=CAPTURE_ONLY" +
					"&x_auth_code=" + authCode;
			}
			else
			{
				request += "&x_type=PRIOR_AUTH_CAPTURE" +
					"&x_trans_id=" + processorTransactionID;
			}
			StringDictionary results = readHtmlPage( request );

			responseCode = results["ResponseReasonCode"];
			responseText = results["ResponseReasonText"];
			AVSResult = results["AVSResultCode"];
			CVV2Response = results["CVV2ResponseCode"];
			processorTransactionID = results["TransactionID"];

			return (results["ResponseCode"] == "1");
		}

		public override bool Refund( CustomerInfo customerInfo, 
			Int32 transactionID, 
			decimal amount, 
			string processorTransactionID,
			out string responseCode,
			out string responseText
			)
		{
            if (customerInfo.CardExpiration == null || customerInfo.CardExpiration == string.Empty)
            {
				responseCode = "7";
				responseText = "Missing expiration date";
				return false;
			}
			string request = 
				"&x_type=CREDIT" +
				"&x_amount=" + amount.ToString() +
				"&x_invoice_num=" + transactionID.ToString() +
				"&x_card_num=" + customerInfo.CardNumber +
				"&x_exp_date=" + customerInfo.CardExpiration +
				"&x_trans_id=" + processorTransactionID;
			StringDictionary results = readHtmlPage( request );

			responseCode = results["ResponseReasonCode"];
			responseText = results["ResponseReasonText"];

			return (results["ResponseCode"] == "1");
		}

		private StringDictionary readHtmlPage(string request)
		{
			StringDictionary d = new StringDictionary();

			String result = "";
			String strPost = "x_login=" + _loginID + 
				"&x_tran_key=" + _transactionKey +
				"&x_method=CC" + 
				"&x_version=3.1" +
				"&x_delim_data=TRUE" +
				"&x_delim_data=TRUE" +
				"&x_delim_char=|";
			if ( _test )
				strPost += "&x_test_request=TRUE";
			strPost += request;
			StreamWriter myWriter = null;
      
			HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(_url);
			objRequest.Method = "POST";
			objRequest.ContentLength = strPost.Length;
			objRequest.ContentType = "application/x-www-form-urlencoded";
      
			try
			{
				myWriter = new StreamWriter(objRequest.GetRequestStream());
				myWriter.Write(strPost);
			}
			catch (Exception e) 
			{
				d.Add("ResponseCode", "3");
				d.Add("ResponseReasonCode", "500");
				d.Add("ResponseReasonText", e.Message);
				return d;
			}
			finally 
			{
				myWriter.Close();
			}
         
			HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse();
			using (StreamReader sr = 
					   new StreamReader(objResponse.GetResponseStream()) )
			{
				result = sr.ReadToEnd();

				// Close and clean up the StreamReader
				sr.Close();
			}

			string[] results = result.Split('|');

			if ( results.Length < 68 )
			{
				d.Add("ResponseCode", "3");
				d.Add("ResponseReasonCode", "500");
				d.Add("ResponseReasonText", "Insuficient response received from Authorize.Net Gateway" );
				return d;
			}

			d.Add("ResponseCode", results[0]);
			d.Add("ResponseSubCode", results[1]);
			d.Add("ResponseReasonCode", results[2]);
			d.Add("ResponseReasonText", results[3]);
			d.Add("ApprovalCode", results[4]);
			d.Add("AVSResultCode", results[5]);
			d.Add("TransactionID", results[6]);
			d.Add("CVV2ResponseCode", results[38]);
			d.Add("CAVVResponseCode", results[39]);

			return d;
		}

        string UrlEncode(string value)
        {
            return value.Replace("%", "%25")
                .Replace("$", "%24")
                .Replace("&", "%26")
                .Replace("+", "%2B")
                .Replace(",", "%2C")
                .Replace("/", "%2F")
                .Replace(":", "%3A")
                .Replace(";", "%3B")
                .Replace("=", "%3D")
                .Replace("?", "%3F")
                .Replace("@", "%40")
                .Replace(" ", "+")
                .Replace("\"", "%22")
                .Replace("<", "%3C")
                .Replace(">", "%3E")
                .Replace("#", "%23")
                .Replace("{", "%7B")
                .Replace("}", "%7D")
                .Replace("|", "%7C")
                .Replace("\\", "%5C")
                .Replace("^", "%5E")
                .Replace("~", "%7E")
                .Replace("[", "%5B")
                .Replace("]", "%5D")
                .Replace("`", "%60");
        }
	}
}
